package com.ml4ai.backend.services.impl;

import com.ml4ai.backend.domain.DbMap;
import com.ml4ai.backend.repository.DbMapRepository;
import com.ml4ai.backend.services.DbMapService;
import com.ml4ai.backend.utils.SpringUtils;
import lombok.Data;
import org.apache.commons.lang3.NotImplementedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Created by leecheng on 2018/10/27.
 */
@Service
@Transactional
public class DbMapServiceImpl implements com.ml4ai.backend.services.DbMapService {

    @Autowired
    DbMapRepository dbMapRepository;

    @Override
    public Map<String, String> generateLocalMapWrapper(String name) {
        return DbMapWrapper.getInstance(name);
    }

    public Integer countByName(String name) {
        return dbMapRepository.countByNameAndStatus(name, "1");
    }

    public Integer countByNameAndKey(String name, String key) {
        return dbMapRepository.countByNameAndKeyAndStatus(name, key, "1");
    }

    public Integer countByNameAndValue(String name, String val) {
        return this.dbMapRepository.countByNameAndValueAndStatus(name, val, "1");
    }

    public DbMap getByNameAndKey(String name, String key) {
        return dbMapRepository.getByNameAndKeyAndStatus(name, key, String.valueOf(1));
    }

    public <T, R> R execute(Function<T, R> function, T t) {
        return function.apply(t);
    }

    @Data
    private static class DbMapWrapper implements Map<String, String> {

        private DbMapService dbMapService;
        private DbMapRepository dbMapRepository;
        private String name;

        public static DbMapWrapper getInstance(String name) {
            DbMapWrapper map = new DbMapWrapper(name);
            return map;
        }

        private DbMapWrapper(String name) {
            this.name = name;
            this.dbMapService = SpringUtils.getService(DbMapService.class);
            this.dbMapRepository = SpringUtils.getService(DbMapRepository.class);
        }

        @Override
        public int size() {
            return dbMapService.countByName(name);
        }

        @Override
        public boolean isEmpty() {
            return dbMapService.countByName(name).intValue() == 0;
        }

        @Override
        public boolean containsKey(Object key) {
            return dbMapService.countByNameAndKey(name, (String) key).intValue() > 0;
        }

        @Override
        public boolean containsValue(Object value) {
            return dbMapService.countByNameAndValue(name, (String) value).intValue() > 0;
        }

        @Override
        public String get(Object key) {
            DbMap dbMap = dbMapService.getByNameAndKey(name, (String) key);
            if (dbMap != null) {
                return dbMap.getValue();
            } else {
                return null;
            }
        }

        @Override
        public String put(String key, String value) {
            String ret = dbMapService.execute((field) -> {
                DbMap dbMap = dbMapRepository.getByNameAndKeyAndStatus(name, field, Integer.valueOf(1).toString());
                if (dbMap == null) {
                    dbMap = DbMap.builder().name(name).key(field).value(value).build();
                    dbMap.setStatus("1");
                } else {
                    dbMap.setValue(value);
                }
                dbMap = dbMapRepository.save(dbMap);
                return dbMap.getValue();
            }, key);
            return ret;
        }

        @Override
        public String remove(Object key) {
            return dbMapService.execute((field) -> {
                String value = get(field);
                dbMapRepository.deleteByNameAndKeyAndStatus(name, (String) field, "1");
                return value;
            }, key);
        }

        @Override
        public void putAll(Map<? extends String, ? extends String> m) {
            for (String key : m.keySet()) {
                put(key, m.get(key));
            }
        }

        @Override
        public void clear() {
            dbMapService.execute(map -> {
                dbMapRepository.deleteByNameAndStatus(map, "1");
                return null;
            }, name);
        }

        @Override
        public Set<String> keySet() {
            return dbMapService.execute(map -> dbMapRepository.getByNameAndStatus(map, "1").stream().map(DbMap::getKey).collect(Collectors.toSet()), name);
        }

        @Override
        public Collection<String> values() {
            return dbMapService.execute(map -> dbMapRepository.getByNameAndStatus(map, "1").stream().map(DbMap::getValue).collect(Collectors.toSet()), name);
        }

        @Override
        public Set<Entry<String, String>> entrySet() {
            return dbMapService.execute(map -> dbMapRepository.getByNameAndStatus(map, "1").stream().map(
                    dbMap -> {
                        return (Map.Entry<String, String>) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{Map.Entry.class}, (proxy, method, args) -> {
                            switch (method.getName().toLowerCase()) {
                                case "getkey":
                                    return dbMap.getKey();
                                case "getvalue":
                                    return dbMap.getValue();
                                case "hashcode":
                                    return dbMap.hashCode();
                                default:
                                    throw new NotImplementedException("未实现该方法" + method.getName());
                            }
                        });
                    }
            ).collect(Collectors.toSet()), name);
        }
    }

}

